package com.ruoyi.lowcode.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.constant.CacheNames;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.redis.CacheUtils;
import com.ruoyi.lowcode.common.Lowcode;
import com.ruoyi.lowcode.database.DatabaseMain;
import com.ruoyi.lowcode.database.SourceFactory;
import com.ruoyi.lowcode.domain.LcField;
import com.ruoyi.lowcode.domain.bo.ModelRowBo;
import com.ruoyi.lowcode.domain.bo.SelectListModelBo;
import com.ruoyi.lowcode.domain.vo.*;
import com.ruoyi.lowcode.enums.MysqlConditionalConstructor;
import com.ruoyi.lowcode.jsqlparser.JSqlParserUtils;
import com.ruoyi.lowcode.jsqlparser.NormalSqlField;
import com.ruoyi.lowcode.jsqlparser.NormalSqlStructure;
import com.ruoyi.lowcode.mapper.LcDatabaseSourceMapper;
import com.ruoyi.lowcode.mapper.LcDictionaryMapper;
import com.ruoyi.lowcode.mapper.LcFieldMapper;
import com.ruoyi.lowcode.sqlgenerator.GeneratorFactory;
import com.ruoyi.lowcode.sqlgenerator.GeneratorMain;
import com.ruoyi.lowcode.utils.ConverterUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import com.ruoyi.lowcode.domain.bo.LcModelBo;
import com.ruoyi.lowcode.domain.LcModel;
import com.ruoyi.lowcode.mapper.LcModelMapper;
import com.ruoyi.lowcode.service.ILcModelService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 模型Service业务层处理
 *
 * @author ruoyi
 * @date 2023-09-30
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class LcModelServiceImpl implements ILcModelService {

    private final LcModelMapper baseMapper;

    private final LcFieldMapper fieldMapper;

    private final LcDatabaseSourceMapper databaseSourceMapper;

    private final Lowcode lowcode;

    @Override
    public List<ModelInfoVo> getModelSon(Long modelId) {
        List<LcFieldVo> queryFieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("parent_model_id", modelId)
                        .eq("del_flag", "0")
        );

        List<ModelInfoVo> modelInfoList = new ArrayList<>();

        for (LcFieldVo field : queryFieldList){
            ModelInfoVo modelInfoVo = getTableModel(field.getModelId());
            if(ObjectUtil.isNotNull(modelInfoVo)){
                modelInfoVo.setParentModelField(field);
                modelInfoList.add(modelInfoVo);
            }
        }

       return modelInfoList;
    }

    @Override
    @Transactional
    public Boolean addOrUpdateModelRow(ModelRowBo modelRowBo) throws ClassNotFoundException {

        LcModelVo model = baseMapper.selectVoById(modelRowBo.getModelId());

        List<LcFieldVo> fieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", modelRowBo.getModelId())
                        .eq("del_flag", "0")
        );

        Map<String, String> tableIds = getTableIds(modelRowBo.getModelId());

        LcDatabaseSourceVo databaseSource = databaseSourceMapper.selectVoById(model.getDatabaseSourceId());

        SourceFactory sourceFactory = DatabaseMain.getSource(databaseSource.getType());
        GeneratorFactory generatorFactory = GeneratorMain.getGenerator(databaseSource.getType());

        List<String> sqlList = new ArrayList<>();

        if (ObjectUtils.isNotEmpty(modelRowBo.getRowInfo())){
            // 修改
            sqlList = generatorFactory.update(databaseSource, fieldList, modelRowBo, tableIds);
        }else {
            // 新增
            sqlList = generatorFactory.insert(databaseSource, fieldList, modelRowBo, tableIds);
        }

        // 遍历修改数据
        for (String updateSql : sqlList) {
            sourceFactory.update(databaseSource, updateSql);
        }

        return true;
    }

    @Override
    public Boolean deleteModelRow(ModelRowBo modelRowBo) throws ClassNotFoundException {

        // 逻辑删除可以自行处理

        LcModelVo model = baseMapper.selectVoById(modelRowBo.getModelId());

        // 查询所有主键字段
        List<LcFieldVo> fieldIdList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", modelRowBo.getModelId())
                        .eq("is_id", "1")
                        .eq("del_flag", "0")
        );

        LcDatabaseSourceVo databaseSource = databaseSourceMapper.selectVoById(model.getDatabaseSourceId());

        SourceFactory sourceFactory = DatabaseMain.getSource(databaseSource.getType());
        GeneratorFactory generatorFactory = GeneratorMain.getGenerator(databaseSource.getType());

        for (LcFieldVo fieldVo : fieldIdList){

            String deleteSql = generatorFactory.delete(fieldVo.getTableName(), fieldVo.getFieldName(), modelRowBo.getRowInfo().get(fieldVo.getFieldAlias()));

            sourceFactory.update(databaseSource, deleteSql);
        }

        return true;
    }

    /**
     * 获取模型所有表和对应主键
     *
     * @param modelId
     * @return
     */
    private Map<String, String> getTableIds(Long modelId){
        // 查询所有主键字段
        List<LcFieldVo> fieldIdList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", modelId)
                        .eq("is_id", "1")
                        .eq("del_flag", "0")
        );

        Map<String, String> tableIds = new HashMap<>();
        for (LcFieldVo fieldVo : fieldIdList){
            tableIds.put(fieldVo.getTableName(), fieldVo.getFieldAlias());
        }

        return tableIds;
    }

    /**
     * 查询模型
     */
    @Override
    public LcModelVo queryById(Long modelId) {
        return baseMapper.selectVoById(modelId);
    }

    @Override
    public Boolean initializationField(Long modelId) {

        LcModelVo modelVo = baseMapper.selectVoById(modelId);

        try {
            NormalSqlStructure normalSqlStructure = CacheUtils.get(CacheNames.LOW_CODE, "model_select_sql:" + modelId);
            if (ObjectUtils.isEmpty(normalSqlStructure)) {
                normalSqlStructure = JSqlParserUtils.getStructure(modelVo.getSelectSql());
            }

            List<NormalSqlField> normalSqlFieldList = normalSqlStructure.getFieldList();
            List<LcField> lcFieldList = new ArrayList<>();
            for (NormalSqlField normalSqlField : normalSqlFieldList) {
                LcField lcField = new LcField();
                lcField.setModelId(modelId);
                lcField.setFieldName(normalSqlField.getFieldName());
                lcField.setFieldAlias(normalSqlField.getAlias());
                lcField.setFieldTableAliasName(normalSqlField.getFieldTableAliasName());
                lcField.setTableName(normalSqlField.getTable());
                lcField.setIsId("0");
                lcField.setIsQuery("1");
                lcField.setIsSearch("0");
                lcField.setIsEmpty("1");
                lcField.setIsPicture("0");
                lcField.setModelName(normalSqlField.getParentTableName());
                lcField.setModelFieldId(normalSqlField.getParentFieldName());
                lcFieldList.add(lcField);
            }
            fieldMapper.delete(new QueryWrapper<LcField>().eq("model_id", modelId));
            fieldMapper.insertBatch(lcFieldList);
            return true;
        } catch (JSQLParserException e) {
            log.info("SQL解析出错，原因" + e.getMessage());
            throw new RuntimeException("SQL解析出错，请检测SQL。");
        }
    }

    /**
     * 查询模型列表
     */
    @Override
    public TableDataInfo<LcModelVo> queryPageList(LcModelBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<LcModel> lqw = buildQueryWrapper(bo);
        Page<LcModelVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public ModelInfoVo getTableModel(Long modelId) {
        ModelInfoVo modelInfo = new ModelInfoVo();
        LcModelVo model = baseMapper.selectVoById(modelId);

        if(ObjectUtil.isNull(model)){
            return null;
        }

        modelInfo.setModel(model);

        List<LcFieldVo> queryFieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                .eq("model_id", modelId)
                .eq("is_query", "1")
                .eq("del_flag", "0")
        );

        queryFieldList.forEach( field -> {
            lowcode.fieldDictionary(field);
        });

        List<LcFieldVo> searchFieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", modelId)
                        .eq("is_search", "1")
                        .isNull("parent_model_id")
                        .eq("del_flag", "0")
        );

        searchFieldList.forEach( field -> {
            lowcode.fieldDictionary(field);
        });

        modelInfo.setQueryFieldList(queryFieldList);
        modelInfo.setSearchFieldList(searchFieldList);
        return modelInfo;
    }

    @Override
    public List<FormModelFieldVo> getFormModelField(Long modelId) {
        List<LcFieldVo> searchFieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", modelId)
                        .eq("is_id", "0")
                        .eq("del_flag", "0")
        );

        List<FormModelFieldVo> formModelFieldVoList = new ArrayList<>();

        searchFieldList.forEach( field -> {
            lowcode.fieldDictionary(field);
            FormModelFieldVo formModelFieldVo = new FormModelFieldVo();
            formModelFieldVo.setFieldLabel(field.getRemark());
            formModelFieldVo.setFieldName(field.getFieldAlias());
            formModelFieldVo.setIsPicture(field.getIsPicture());
            formModelFieldVo.setDictionaryInfo(field.getDictionaryInfo());
            formModelFieldVoList.add(formModelFieldVo);
        });

        return formModelFieldVoList;
    }



    @Override
    public TableDataInfo<Map<String, Object>> selectListByModel(SelectListModelBo selectListModelBo) throws JSQLParserException, ClassNotFoundException {
        LcModel model = baseMapper.selectById(selectListModelBo.getModelId());

        List<LcField> fieldList = fieldMapper.selectList(
                new QueryWrapper<LcField>()
                        .eq("model_id", model.getModelId())
                        .eq("is_search", "1")
                        .eq("del_flag", "0")
        );

        LcDatabaseSourceVo databaseSource = databaseSourceMapper.selectVoById(model.getDatabaseSourceId());

        GeneratorFactory generatorFactory = GeneratorMain.getGenerator(databaseSource.getType());
        String conditionalSql = generatorFactory.conditionalSql(selectListModelBo, model, fieldList);
        String pageSql = generatorFactory.paging(selectListModelBo.getPageQuery());

        List<Map<String, Object>> dataList =
                DatabaseMain.getSource(databaseSource.getType()).selectList(databaseSource,
                        model.getSelectSql().replace("#WHERESQL", conditionalSql) + pageSql);

        TableDataInfo<Map<String, Object>> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(dataList);
        rspData.setTotal(Long.valueOf(DatabaseMain.getSource(databaseSource.getType()).selectList(databaseSource,
                model.getCountSql().replace("#WHERESQL", conditionalSql)).get(0).get("total").toString()));
        return rspData;
    }

    @Override
    public ExportModelVo exportByModel(SelectListModelBo selectListModelBo) throws JSQLParserException, ClassNotFoundException {
        LcModel model = baseMapper.selectById(selectListModelBo.getModelId());

        List<LcField> fieldList = fieldMapper.selectList(
                new QueryWrapper<LcField>()
                        .eq("model_id", model.getModelId())
                        .eq("is_search", "1")
                        .eq("del_flag", "0")
        );

        LcDatabaseSourceVo databaseSource = databaseSourceMapper.selectVoById(model.getDatabaseSourceId());
        GeneratorFactory generatorFactory = GeneratorMain.getGenerator(databaseSource.getType());

        String conditionalSql = generatorFactory.conditionalSql(selectListModelBo, model, fieldList);

        // 获取查询字段
        List<LcFieldVo> queryFieldList = fieldMapper.selectVoList(
                new QueryWrapper<LcField>()
                        .eq("model_id", selectListModelBo.getModelId())
                        .eq("is_query", "1")
                        .eq("del_flag", "0")
        );

        String selectSql = generatorFactory.getSelectFieldSql(queryFieldList) + " "
                + generatorFactory.getSelectWhereSql(model.getSelectSql());

        List<Map<String, Object>> dataList =
                DatabaseMain.getSource(databaseSource.getType()).selectList(databaseSource,
                        selectSql.replace("#WHERESQL", conditionalSql));

        ExportModelVo exportModel = new ExportModelVo();
        exportModel.setFieldList(queryFieldList);
        exportModel.setDataList(dataList);
        exportModel.setModel(model);

        return exportModel;
    }

    /**
     * 查询模型列表
     */
    @Override
    public List<LcModelVo> queryList(LcModelBo bo) {
        LambdaQueryWrapper<LcModel> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<LcModel> buildQueryWrapper(LcModelBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<LcModel> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getModelId() != null, LcModel::getModelId, bo.getModelId());
        lqw.like(StringUtils.isNotBlank(bo.getModelName()), LcModel::getModelName, bo.getModelName());
        lqw.eq(StringUtils.isNotBlank(bo.getSelectSql()), LcModel::getSelectSql, bo.getSelectSql());
        lqw.eq(StringUtils.isNotBlank(bo.getDeleteSql()), LcModel::getDeleteSql, bo.getDeleteSql());
        return lqw;
    }

    /**
     * 新增模型
     */
    @Override
    public Boolean insertByBo(LcModelBo bo) {
        NormalSqlStructure normalSqlStructure = checkSelectSql(bo);
        LcModel add = BeanUtil.toBean(bo, LcModel.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setModelId(add.getModelId());
            CacheUtils.put(CacheNames.LOW_CODE, "model_select_sql:" + add.getModelId(), normalSqlStructure);
        }
        return flag;
    }

    /**
     * 修改模型
     */
    @Override
    public Boolean updateByBo(LcModelBo bo) {
        NormalSqlStructure normalSqlStructure = checkSelectSql(bo);
        LcModel update = BeanUtil.toBean(bo, LcModel.class);
        validEntityBeforeSave(update);
        if (baseMapper.updateById(update) > 0) {
            CacheUtils.put(CacheNames.LOW_CODE, "model_select_sql:" + update.getModelId(), normalSqlStructure);
            return true;
        }
        return false;
    }

    public NormalSqlStructure checkSelectSql(LcModelBo bo) {
        try {
            NormalSqlStructure normalSqlStructure = JSqlParserUtils.getStructure(bo.getSelectSql());
            if (ObjectUtils.isEmpty(normalSqlStructure)) {
                throw new RuntimeException("查询SQL解析出错，请检测SQL。");
            }
            return normalSqlStructure;
        } catch (JSQLParserException e) {
            log.info("SQL解析出错，原因" + e.getMessage());
            throw new RuntimeException("查询SQL解析出错，请检测SQL。");
        }
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(LcModel entity) {
        //TODO 做一些数据校验,如唯一约束
        if (StringUtils.isNotBlank(entity.getUrl())) {
            LcModel model = baseMapper.selectOne(
                    new QueryWrapper<LcModel>()
                            .eq("del_flag", "0")
                            .eq("url", entity.getUrl())
                            .ne(entity.getModelId() != null, "model_id", entity.getModelId())
            );
            if (ObjectUtil.isNotNull(model)) {
                throw new RuntimeException("路由已存在");
            }
        }
    }

    /**
     * 批量删除模型
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
